package org.hsc.proxy;
/*
 * Cache.java
 *
 * Created on April 14, 2008
 *
 * Name:        Moty Michaely   |   Alon Halfon
 * ID:          043413087       |   300545969
 */
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map.Entry;
import java.io.IOException;
import java.util.HashMap;

public class Cache {
    public enum CacheResult { 
        Hit, 
        Miss, 
        NA;
         
        public String toString() {
            switch(this) {
                case Hit:   return "Hit";
                case Miss: return "Miss";
                case NA:  return "N/A";
            }
            return null;
        }
    }
    public enum Filtered {Yes, No}
    
    private volatile HashMap  <String,CacheComponent> cacheMap; //contain the last recived files i.e the cache data
    private volatile int maxCacheSize;// the cache size in bytes
    private volatile int maxFileSize; // max file size that will be saved in the cache (in bytes)
    private volatile int currentCacheSize;
    private volatile boolean active;
    
    Cache (int cacheSize,int maxFileSize){
        cacheMap=new HashMap <String,CacheComponent>();
        this.maxCacheSize=cacheSize;
        this.maxFileSize=maxFileSize;
        currentCacheSize=0;
        this.active=false;
    }
    
    /*
     * default c-tor
     * have to set the maxCacheSize and maxFileSize
     */
    Cache(){
        cacheMap=new HashMap <String,CacheComponent>();
        currentCacheSize=0;
        this.active=false;
    }
    public synchronized void turnOn()
    {
        this.active=true;
    }
    public synchronized void turnOff()
    {
        this.active=false;
    }
    public boolean isActive(){
        return active;
    }
    /*
     * setting max CacheSize
     */
    public void setMaxCacheSize(int cacheSize){
        this.maxCacheSize=cacheSize;
    }
    /*
     * setting max file size
     * automatically remove existing files that
     * bigger the the max size
     */
    public synchronized void setMaxFileSize(int fileSize){
        this.maxFileSize=fileSize;
        this.removeBySize(maxFileSize);
    }    
    
    public synchronized void clearCache(){
       this.currentCacheSize=0;
       this.cacheMap.clear();
    }
    
    public synchronized String getModifiedDate(String url){
        return cacheMap.get(url).getLastModified();
    }
    
    public synchronized CacheComponent getCachedComponent(String url){
        // function return CacheComponent class
        // null if not exist
        CacheComponent cache=null;                                      
        cache = cacheMap.get(url);
        return cache;
    }
    
    public synchronized boolean addCacheComponent(String url,HttpResponse response) throws FileTooBigException, IOException{
        CacheComponent component=cacheMap.get(url);
        if (component!=null)// already exist
        {
            CacheComponent oldComponent=cacheMap.remove(url);
            currentCacheSize-=oldComponent.getSize();//remove the old component
        }
        addNewComponent(url,response);
        return true; //add succeed
    }
    
    public synchronized void removeBySize(int maxAllowedSize){
        //for each file TODO
        Iterator<Entry<String, CacheComponent>> iter=cacheMap.entrySet().iterator();
        while (iter.hasNext()) //remove all the files that weighted above the allowed max
        {
            CacheComponent currentComponent=(CacheComponent)iter.next().getValue();
            if (currentComponent.getSize()>maxAllowedSize)
            {// file is too big
                currentCacheSize-=currentComponent.getSize();
                iter.remove();
            }
        }
       /* if (newComponent.getSize()>maxFileSize) // now allow file larger than maxFileSize
        {
           
        }*/
    }
    
    private synchronized boolean addNewComponent(String url,HttpResponse response) throws FileTooBigException, IOException{
    CacheComponent newComponent=new CacheComponent(url,response);
        while ((currentCacheSize+newComponent.getSize()>maxCacheSize)&&(cacheMap.size()>0)){//create space for the new component
        //we stop looping if there are no more files to remove
            
            LinkedHashSet<String> data =(LinkedHashSet<String>) cacheMap.keySet();
            CacheComponent removedComponent=cacheMap.remove((String)data.iterator().next());
//            Log.getInstance().logCache(removedComponent.getUrl(),removedComponent.getSize());
            currentCacheSize-=removedComponent.getSize();//reduce the removed size from the total size
        }
        if (currentCacheSize+newComponent.getSize()<=maxCacheSize)
        {//no problems- there is enough size
            cacheMap.put(url, newComponent);
            currentCacheSize+=newComponent.getSize();
            return true;
        }
        else
        {// still not enough space - the file is too big for caching.
            throw new FileTooBigException();
        }
        
        
    }
    
            
}